﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObviousCode.Aspect.PluginLibrary.Interfaces;
using ObviousCode.Aspect.PluginLibrary.Event;
using System.Drawing;
using ObviousCode.UI.Aspect.PluginLibrary.Interfaces;
using ObviousCode.Aspect.PluginLibrary.Exceptions;

namespace ObviousCode.Aspect.PluginLibrary.Abstract
{
    public class BaseSurfaceRelationship : ISurfaceRelationship
    {
        #region ISurfaceRelationship Members

        IAspectDesigner _host;

        public string InstanceKey { get; private set; }
        public IAspectRelationship Tag { get; set; }
        public IUIRelationshipTheme Theme { get; set; }

        public event EventHandler<AspectObjectBoundsRequestEventArgs> SurfaceObjectPositionRequested;

        public BaseSurfaceRelationship(IAspectDesigner host) 
        {
            _host = host;
            InstanceKey = Guid.NewGuid().ToString();
        }

        public virtual void Render(System.Drawing.Graphics graphics)
        {
            try
            {
                AspectObjectBoundsRequestEventArgs sourceArgs = new AspectObjectBoundsRequestEventArgs(Tag.Source, graphics);
                AspectObjectBoundsRequestEventArgs destinationArgs = new AspectObjectBoundsRequestEventArgs(Tag.Destination, graphics);

                if (SurfaceObjectPositionRequested != null)
                {
                    SurfaceObjectPositionRequested(this, sourceArgs);
                    SurfaceObjectPositionRequested(this, destinationArgs);
                }

                if (!sourceArgs.BoundsLocated || !destinationArgs.BoundsLocated) return;//throw exception?

                DrawRelationship(graphics, sourceArgs.RequestedBounds, destinationArgs.RequestedBounds);
            }
            catch (System.Exception e)
            {            	            
                AspectRenderingException exception = new AspectRenderingException(e);
                exception.GuiltyParty = this;
                throw exception;
            }                        
        }

        protected virtual void DrawRelationship(System.Drawing.Graphics graphics, RectangleF source, RectangleF destination)
        {
            DrawRelationshipLine(graphics, source, destination);
            DrawRelationshipStartCap(graphics, source);
            DrawRelationshipEndCap(graphics, destination);
        }        

        private void DrawRelationshipLine(Graphics graphics, RectangleF source, RectangleF destination)
        {
            PointF sourceAnchor = CalculateAnchorPointPosition(DefineAnchorPoint(source, destination), source);
            PointF destinationAnchor = CalculateAnchorPointPosition(DefineAnchorPoint(destination, source), destination);

            //Temporary, until I get around to custom drawing caps
            Pen pen = new Pen(Theme.LineColor, Theme.LineThickness);

            pen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
            pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;

            graphics.DrawLine(pen, sourceAnchor, destinationAnchor);
        }

        protected virtual void DrawRelationshipStartCap(Graphics graphics, RectangleF source)
        {
            
        }

        protected virtual void DrawRelationshipEndCap(Graphics graphics, RectangleF destination)
        {
            
        }

        public static AnchorPoints DefineAnchorPoint(RectangleF source, RectangleF destination)
        {
            AnchorPoints point = AnchorPoints.None;

            RectangleF topLeftQuadrant = new RectangleF(0f, 0f, source.Left, source.Top);
            RectangleF topCenterQuadrant = new RectangleF(source.Left, 0f, source.Width, source.Top);
            RectangleF topRightQuadrant = new RectangleF(source.Right, 0f, float.MaxValue, source.Top);

            RectangleF middleLeftQuadrant = new RectangleF(0f, source.Top, source.Left, source.Height);
            RectangleF middleRightQuadrant = new RectangleF(source.Right, source.Top, float.MaxValue, source.Height);

            RectangleF bottomLeftQuadrant = new RectangleF(0f, source.Bottom, source.Left, float.MaxValue);
            RectangleF bottomCenterQuadrant = new RectangleF(source.Left, source.Bottom, source.Width, float.MaxValue);
            RectangleF bottomRightQuadrant = new RectangleF(source.Right, source.Bottom, float.MaxValue, float.MaxValue);

            if (destination.IntersectsWith(topCenterQuadrant))
            {
                point = AnchorPoints.Top;
            }
            else if (destination.IntersectsWith(middleRightQuadrant))
            {
                point = AnchorPoints.Right;
            }
            else if (destination.IntersectsWith(bottomCenterQuadrant))
            {
                point = AnchorPoints.Bottom;
            }
            else if (destination.IntersectsWith(middleLeftQuadrant))
            {
                point = AnchorPoints.Left;
            }
            else if (destination.IntersectsWith(topRightQuadrant))
            {
                point = AnchorPoints.TopRight;
            }
            else if (destination.IntersectsWith(bottomRightQuadrant))
            {
                point = AnchorPoints.BottomRight;
            }
            else if (destination.IntersectsWith(bottomLeftQuadrant))
            {
                point = AnchorPoints.BottomLeft;
            }
            else if (destination.IntersectsWith(topLeftQuadrant))
            {
                point = AnchorPoints.TopLeft;
            }
            
            return point;
        }

        public PointF CalculateAnchorPointPosition(AnchorPoints point, RectangleF bounds)
        {
            float xCenter = bounds.Left + (bounds.Width / 2);
            float yCenter = bounds.Top + (bounds.Height / 2);
            switch (point)
            {
                case AnchorPoints.Left:
                    return new PointF(bounds.Left, yCenter);
                case AnchorPoints.Right:
                    return new PointF(bounds.Right, yCenter);
                case AnchorPoints.Top:
                    return new PointF(xCenter, bounds.Top);
                case AnchorPoints.Bottom:
                    return new PointF(xCenter, bounds.Bottom);
                case AnchorPoints.TopLeft:
                    return new PointF(bounds.Left, bounds.Top);
                case AnchorPoints.TopRight:
                    return new PointF(bounds.Right, bounds.Top);
                case AnchorPoints.BottomLeft:
                    return new PointF(bounds.Left, bounds.Bottom);
                case AnchorPoints.BottomRight:
                    return new PointF(bounds.Right, bounds.Bottom);
                default:
                    return new PointF(xCenter, yCenter);
            }
        }

        #endregion
    }
}
